當第一次使用 del.icio.us 社會性書籤 時,分享書籤並不稀奇,神奇的是透過 Tag 標籤的功能,更能發現到別人在相同議題上的搜集,而更多人的 Tag 同一個項目,就更看到該項目的價值。所以就夢想著,自己也可在自己所建置的系統中,有 Tagging 的功能。然而在 RoR 中可以很省力地建立此功能。
本篇目標是先建立不區分 User 的標籤系統,了解一下如何建置,之後再建立可區分哪些 User 建立哪些標籤的系統。
安裝 be_taggable
RoR 中有關 tag 相關的 plugins 也有好幾種,在此採用 be_taggable ,並且以第三日 [RoR] 簡單完成 CRUD 的動作 中的最簡單的 Blog 裡的 Article 做為標籤的對象:
cd /home/ironman/test
./script/plugin install http://railers.rubyforge.org/svn/plugins/trunk/be_taggable
script/generate be_taggable_tables Article
# 其中會產生 db/migrate/20081009xxxx_add_be_taggable.rb ,裡面會有些錯誤,修改:
# 將 add_column articles, :tags_cache, :string, :default => "--- []" 這一行改為
add_column :articles, :tags_cache, :string, :default => "--- []"
# 將 remove_column articles, :tags_cache 改為
remove_column :articles, :tags_cache
# 再執行 才不會因上述的問題 而出現錯誤訊息
rake db:migrate
Console 端操作測試 Tag 功能
在要被 tag 的 Model 中,加入設定,在此編輯 app/models/article.rb,加入 be_taggable 的字樣:
class Article < ActiveRecord::Base
be_taggable
before_save :generate_permalink
...
執行 ./script/console
# 以第一篇文來加標籤
>> a = Article.find(1)
=> #<Article id: 1, title: "Save the Cheerleader, Save the World", body: "Save the Cheerleader, Save the World\r\nSave the Chee...", created_at: "2008-09-11 00:00:00", updated_at: "2008-10-02 08:28:40", permalink: "save-the-cheerleader-save-the-world", tags_cache: "--- []">
>> a.tag('影集,英雄,Heroes')
=> true
# 再以另一篇文章來加標籤
>> b=Article.find(11)
=> #<Article id: 11, title: "救啦啦隊長,救世界", body: "一群平凡如你我的人們 卻擁有特殊基?...", created_at: "2008-10-02 20:12:27", updated_at: "2008-10-02 20:12:27", permalink: "jiu-la-la-dui-chang-jiu-shi-jie", tags_cache: "--- []">
>> b.tag('Heroes,英雄')
=> true
# 第11篇文,已把剛標籤的內容放在 tags_cache 之中
>> b.tags_cache
=> "--- \n- heroes\n- !binary |\n 6Iux6ZuE\n\n"
# 尋找有以 '英雄' 為標籤的文章
>> Article.find_tagged_with('英雄')
=> [#<Article id: 1, title: "Save the Cheerleader, Save the World", body: "Save the Cheerleader, Save the World\r\nSave the Chee...", created_at: "2008-09-11 00:00:00", updated_at: "2008-10-09 16:41:11", permalink: "Save-the-Cheerleader-Save-the-World", tags_cache: "--- \n- heroes\n- !binary |\n 5b2x6ZuG\n\n- !binary |\n ...">, #<Article id: 11, title: "救啦啦隊長,救世界", body: "一群平凡如你我的人們 卻擁有特殊基?...", created_at: "2008-10-02 20:12:27", updated_at: "2008-10-09 16:45:34", permalink: "jiu-la-la-dui-chang-jiu-shi-jie", tags_cache: "--- \n- heroes\n- !binary |\n 6Iux6ZuE\n\n">]
在各文章中顯示已有的標籤
經過上述的測試,接著試著把各文章的標籤,可在 views 中顯示出來:
# 在 app/views/blog/show.html.erb 加入:
標籤:
<% for tag in YAML::load(@article.tags_cache) -%>
<%= tag -%>
<% end -%>
YAML 的語法也使用在 RoR 裡面的所有 *.yml 的檔中,可利用 find ./ -name \*.yml 發現到用 YAML 的檔案;而將 tag 寫到 tags_cache 之中,也減少了許多對資料庫的存取動作。
加入編輯標籤的界面
編輯 app/views/articles/edit.html.erb 加入:
標籤:<br />
<% darray = YAML::load(@article.tags_cache) -%>
<%= text_field_tag 'tag', darray.join(' ') -%>
編輯 app/controllers/articles_controller.rb 在 def update 中加入:
@article.tag(params[:tag].split(' ').join(','))
即可在頁面增減標籤了。由於 be_taggalbe 加入各標籤,是要以','作為區分,而大部人下標籤不習慣去加那個逗號,所以頁面以空個來隔開各標籤的方式來呈獻及輸入,但實際在 controller 的處理裡,把空格改為逗號來寫入標籤。
同樣地在 new 的 view 中加入 tag 的欄位供,也在 controller 中的 create 加入 上述寫入標籤的語法,就提供在寫新文章時,可直接輸入 標籤。
提供標籤的連結
只有顯示標籤還不夠,還要把有相同標籤的文章列出來的功能,希望能夠 http://SITE/tag/英雄 就可列出相關的文章。
在 app/controllers/blog_controller.rb 多加:
def findtag
@title = "以 #{params[:id]} 為標籤的文章"
@prearticles = Article.find_tagged_with(params[:id])
@articles = @prearticles.paginate :per_page => 5, :page => params[:page]
render :template => 'blog/index'
end
#
# 並在 config/routes.rb 加入:
map.tagged_articles 'tag/:id', :controller => 'blog', :action => 'findtag'
可以有上述所提網址的呈獻
上述第2行,多加個標題來辨識
上述第3行,先把有該標籤的文章成一陣列,再交給下一行的 will_paginate 處理顯示
上述第5行,與 blog index 共用同一個 template。
再改一下 app/views/blog/index.html.erb 顯示的方式:
<!-- 是否有 @title 的變數來決定秀出什麼字樣出來 -->
<% if @title.nil? %>
<h2>文章列表</h2>
<% else %>
<h2><%= @title -%></h2>
<% end %>
...
<!-- 將標籤連結加入 -->
<div class="tag">
標籤:
<% for tag in YAML::load(article.tags_cache) -%>
<%= link_to(tag, tagged_articles_path(tag)) -%>
<% end -%>
</div>
這樣就可以有一個不分使用者的簡單標籤系統功能。